package com.liushao.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.liushao.entity.*;
import com.liushao.entity.bo.TransFlowBO;
import com.liushao.entity.vo.BorrowInfoApprovalVO;
import com.liushao.entity.vo.BorrowerDetailVO;
import com.liushao.enums.LendStatusEnum;
import com.liushao.enums.ReturnMethodEnum;
import com.liushao.enums.TransTypeEnum;
import com.liushao.exception.BusinessException;
import com.liushao.hfb.HfbConst;
import com.liushao.hfb.RequestHelper;
import com.liushao.mapper.BorrowerMapper;
import com.liushao.mapper.LendMapper;
import com.liushao.mapper.UserAccountMapper;
import com.liushao.mapper.UserInfoMapper;
import com.liushao.service.*;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.liushao.util.*;
import com.liushao.util.DateUtils;
import com.liushao.util.LendNoUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 * 标的准备表 服务实现类
 * </p>
 *
 * @author chen
 * @since 2025-01-03
 */
@Service
public class LendServiceImpl extends ServiceImpl<LendMapper, Lend> implements LendService {

    @Autowired
    private DictService dictService;

    @Autowired
    private BorrowerMapper borrowerMapper;

    @Autowired
    private BorrowerService borrowerService;

    @Autowired
    private UserInfoMapper userInfoMapper;

    @Autowired
    private TransFlowService transFlowService;

    @Autowired
    private LendItemService lendItemService;

    @Autowired
    private LendReturnService lendReturnService;

    @Autowired
    private LendItemReturnService lendItemReturnService;


    @Override
    public void createLend(BorrowInfoApprovalVO borrowInfoApprovalVO, BorrowInfo borrowInfo) {
        //创建对象 给对象属性赋值
        Lend lend = new Lend();
        lend.setUserId(borrowInfo.getUserId());
        lend.setBorrowInfoId(borrowInfo.getId());
        //生成编号
        lend.setLendNo(LendNoUtils.getLendNo());
        lend.setTitle(borrowInfoApprovalVO.getTitle());
        //标的金额
        lend.setAmount(borrowInfo.getAmount());
        //投资期数
        lend.setPeriod(borrowInfo.getPeriod());
        //年化利率

        // lend.setLendYearRate(borrowInfo.getBorrowYearRate().divide(new BigDecimal(100)));  //错误代码
        lend.setLendYearRate(borrowInfoApprovalVO.getLendYearRate().divide(new BigDecimal(100)));//正确
        //平台服务费率
        lend.setServiceRate(borrowInfoApprovalVO.getServiceRate().divide(new BigDecimal(100)));
        //还款方式
        lend.setReturnMethod(borrowInfo.getReturnMethod());
        //最低投资金额
        lend.setLowestAmount(new BigDecimal(100));
        //已投金额
        lend.setInvestAmount(new BigDecimal(0));
        // 投资人数
        lend.setInvestNum(0);
        //发布日期
        lend.setPublishDate(new Date());
        //开始日期
        //起息日期

        DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd");
        LocalDate lendStartDate = LocalDate.parse(borrowInfoApprovalVO.getLendStartDate(), dtf);
        lend.setLendStartDate(DateUtils.asDate(lendStartDate));


        // 结束时间  起息日期   2025-08-10     12期    2024-08-10
        LocalDate lendEndDate =  lendStartDate.plusMonths(borrowInfo.getPeriod());
        lend.setLendEndDate(DateUtils.asDate(lendEndDate));

        lend.setLendInfo(borrowInfoApprovalVO.getLendInfo());//说明


        //平台收益   年化率 /12  平台每个月的收益  平台服务费率
        //96.12     -->    dount (int)money =
        BigDecimal monthRate= lend.getServiceRate().divide(new BigDecimal(12),8,BigDecimal.ROUND_DOWN);

        //平Expected   = 标的金额*平台每月的服务费率 * 期数
        BigDecimal  expectedAmount= lend.getAmount().multiply(monthRate).multiply(new BigDecimal(lend.getPeriod()));
        lend.setExpectAmount(expectedAmount); //平台预期收益

        lend.setRealAmount(new BigDecimal(0));//实际收益
        lend.setStatus(LendStatusEnum.INVEST_RUN.getStatus());//状态
        //审核时间
        lend.setCheckTime(new Date());
        lend.setCheckAdminId(1L);//审核用户id

        baseMapper.insert(lend);




    }

    @Override
    public List<Lend> selectList() {

        List<Lend> lendList = baseMapper.selectList(null);
        lendList.forEach(lend -> {
            String returnMethod = dictService.
                    getNameByParentDictCodeAndValue("returnMethod",
                            lend.getReturnMethod());
            String msgStatus = LendStatusEnum.getMsgStatus(lend.getStatus());

            lend.getParam().put("returnMethod",returnMethod);
            lend.getParam().put("status",msgStatus);

        });

        return lendList;
    }

    @Override
    public Map<String, Object> getLendDetailById(Long id) {
        //通过id或者标的对象
        Lend lend = baseMapper.selectById(id);

        //组装数据
        String returnMethod = dictService.
                getNameByParentDictCodeAndValue("returnMethod",
                        lend.getReturnMethod());
        String msgStatus = LendStatusEnum.getMsgStatus(lend.getStatus());

        lend.getParam().put("returnMethod",returnMethod);
        lend.getParam().put("status",msgStatus);

        //根据user_id 获取借款人对象
        QueryWrapper<Borrower> wrapper = new QueryWrapper<>();
        wrapper.eq("user_id",lend.getUserId());
        Borrower borrower = borrowerMapper.selectOne(wrapper);

        //组装借款人对象
        BorrowerDetailVO borrowerDetailVOById =
                borrowerService.getBorrowerDetailVOById(borrower.getId());

        //使用map组装数据
        Map<String,Object> map = new HashMap<>();
        map.put("borrower",borrowerDetailVOById);
        map.put("lend",lend);


        return map;
    }

    @Override
    public BigDecimal getInterestCount(BigDecimal investAmount,
                                       BigDecimal lendYearRate,
                                       Integer period, Integer returnMethod) {

        BigDecimal money =null;

        if(returnMethod.intValue()== ReturnMethodEnum.ONE.getMethod()){
            money= Amount1Helper.getInterestCount(investAmount,lendYearRate,period);
        }else   if(returnMethod.intValue()== ReturnMethodEnum.TWO.getMethod()){
            money= Amount2Helper.getInterestCount(investAmount,lendYearRate,period);
        }else   if(returnMethod.intValue()== ReturnMethodEnum.THREE.getMethod()){
            money= Amount3Helper.getInterestCount(investAmount,lendYearRate,period);
        }else   if(returnMethod.intValue()== ReturnMethodEnum.FOUR.getMethod()){
            money= Amount4Helper.getInterestCount(investAmount,lendYearRate,period);
        }


        return money;
    }


    @Autowired
    private UserAccountMapper userAccountMapper;

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void makeLoan(Long id) {// id == lendId
        //获取标的的信息
        Lend lend = baseMapper.selectById(id);

        //需要一些数据
        Map<String,Object> map = new HashMap<>();
        map.put("agentId", HfbConst.AGENT_ID);
        map.put("agentProjectCode",lend.getLendNo());//标的编号
        String loanNo = LendNoUtils.getLoanNo();
        map.put("agentBillNo",loanNo);//放款编号

        //平台收益   放款就扣除  借款人实际借款金额 = 借款金额 - 平台收益
       BigDecimal monthRate= lend.getLendYearRate().
               divide(new BigDecimal(12),8,BigDecimal.ROUND_DOWN);
       //平台实际的收益    = 已经投资的金额  * 月收益 * 标的的期数
      BigDecimal realAmount=  lend.getInvestAmount().multiply(monthRate).
              multiply(new BigDecimal(lend.getPeriod()));
        System.out.println(realAmount);
      //手续费 就是平台收益
        map.put("mchFee",realAmount);
        map.put("timestamp", RequestHelper.getTimestamp());

        String sign = RequestHelper.getSign(map);
        map.put("sign",sign);

        System.out.println("放款参数:"+ JSONObject.toJSONString(map));
        //发送远程调用
        JSONObject result = RequestHelper.sendRequest(map, HfbConst.MAKE_LOAD_URL);

        System.out.println("放款结果:"+ result.toJSONString());
        /*
        result_code	string		是	结果编码：
0000=放款成功
E100=商户授权错误
E101=签名错误
E103=时间戳无效
E104=参数不全或不合法
E105=接收放款失败
U999=未知错误

         */

        if(!"0000".equals(result.getString("resultCode"))){
            try {
                throw  new BusinessException(result.getString("resultMsg"));
            } catch (BusinessException e) {
                throw new RuntimeException(e);
            }
        }

        //更新标的信息
        lend.setRealAmount(realAmount);
        lend.setStatus(LendStatusEnum.PAY_RUN.getStatus());//放款了 就进入还款阶段了
        lend.setPaymentTime(new Date());//放款时间

        baseMapper.updateById(lend);//更新
        //获取借款人信息
        Long userId = lend.getUserId();
        UserInfo userInfo = userInfoMapper.selectById(userId);
         //绑定code
        String bindCode = userInfo.getBindCode();

        BigDecimal total = new BigDecimal(result.getString("voteAmt"));
         //给借款账号赚钱
        userAccountMapper.updateUserAccount(bindCode,total,new BigDecimal(0));
        //新增借款人 交易流水
        /*
              private String agentBillNo;//商户充值订单号
    private BigDecimal amount;//账户的可用余额
    private String bindCode;//绑定
    private TransTypeEnum transTypeEnum;//绑定协议号
    private String memo;//备忘
         */

        TransFlowBO transFlowBO = new TransFlowBO(
                loanNo,
                total,
                bindCode,
                TransTypeEnum.BORROW_BACK,
                "借款放款到账，编号："+lend.getLendNo());

        //别忘记做幂等性  你们自己做

        transFlowService.saveTransFlow(transFlowBO);

        //获取投资列表
       List<LendItem> lendItemList= lendItemService.selectLendById(id,1);

       lendItemList.stream().forEach(item -> {
            //获取投资人信息
           Long investUserId = item.getInvestUserId();

           UserInfo investUserInfo = userInfoMapper.selectById(investUserId);
           String investBindCode = investUserInfo.getBindCode();

           //投资人账号冻结的钱解冻
           BigDecimal investAmount = item.getInvestAmount();
           userAccountMapper.updateUserAccount(investBindCode,new BigDecimal(0),investAmount.negate());

           //新增流水
           TransFlowBO tbo = new TransFlowBO(
                   LendNoUtils.getTransNo(),
                   investAmount,
                   investBindCode,
                   TransTypeEnum.INVEST_UNLOCK,
                   "冻结金额，出借放款，编号："+lend.getLendNo());

           transFlowService.saveTransFlow(tbo);

       });



       // TODO 放款成功了 生成还款计划 和 回款计划

         this.repaymentPlan(lend);//生成还款计划


    }

    private void repaymentPlan(Lend lend) {//生成还款计划
        //还款计划列表
        List<LendReturn> lendReturnList = new ArrayList<>();

        //根据借贷期数 生成还款列表
        int len = lend.getPeriod().intValue();

        for (int i = 1; i <=len ; i++) {
            //创建还款对象
            LendReturn lr = new LendReturn();
            lr.setReturnNo(LendNoUtils.getReturnNo());//还款批次号
            lr.setLendId(lend.getId());//标的的id
            lr.setBorrowInfoId(lend.getBorrowInfoId());      //借款信息id
            //借款人用户id
            lr.setUserId(lend.getUserId());
            //借款金额
            lr.setAmount(lend.getAmount());
            //计息本金额
            lr.setBaseAmount(lend.getInvestAmount());  //投资金额
            //当前的期数
            lr.setCurrentPeriod(i);
            //年化利率
            lr.setLendYearRate(lend.getLendYearRate());
            //还款方式
            lr.setReturnMethod(lend.getReturnMethod());

            // 手续费
            lr.setFee(new BigDecimal(0));

            //还款时指定的还款日期  次月还款
            LocalDate dates = DateUtils.asLocalDate(lend.getLendStartDate());
            dates= dates.plusMonths(i);
            lr.setReturnDate(DateUtils.asDate(dates));
            //是否逾期
            lr.setOverdue(false);
            if(i==len){
                 //最后一个月
                lr.setLast(true);
            }else {
                lr.setLast(false);
            }
              //状态
            lr.setStatus(0);  //未归还


            lendReturnList.add(lr);

        }
        //批量保存数据
        lendReturnService.saveBatch(lendReturnList);
        // 获取 lendReturnList 中还款计划id  和期数应该是对应的关系
        // 还款期数   还款计划的id是对应的map
        Map<Integer,Long> lendReturnMap =
                lendReturnList.stream().collect(
                        Collectors.toMap(LendReturn::getCurrentPeriod,LendReturn::getId));
      //生成回款计划
        List<LendItemReturn> lendItemReturnAllList = new ArrayList<>();
        //获取投资记录
        List<LendItem> lendItemList = lendItemService.selectLendById(lend.getId(),1);

        for(LendItem lendItem:lendItemList){
              //创建回款计划   //需要写一个新的方法
          List<LendItemReturn> lendItemReturnList1=
                  this.returnInvest(lendItem.getId(),lendReturnMap,lend);// TODO 回款计划
          //添加集合 需要使用addAll
          lendItemReturnAllList.addAll(lendItemReturnList1);

        }

        //更新还款计划中的数据
        for (LendReturn lendReturn:lendReturnList){
            //计算 本金
            BigDecimal sumPrincipal= lendItemReturnAllList.stream().filter(
                    item->item.getLendReturnId().longValue()==lendReturn.getId().longValue())
                    .map(LendItemReturn::getPrincipal).reduce(BigDecimal.ZERO,BigDecimal::add);

            //计算 利息
            BigDecimal sumInterest= lendItemReturnAllList.stream().filter(
                            item->item.getLendReturnId().longValue()==lendReturn.getId().longValue())
                    .map(LendItemReturn::getInterest).reduce(BigDecimal.ZERO,BigDecimal::add);
            //本息

            BigDecimal sumTotal= lendItemReturnAllList.stream().filter(
                            item->item.getLendReturnId().longValue()==lendReturn.getId().longValue())
                    .map(LendItemReturn::getTotal).reduce(BigDecimal.ZERO,BigDecimal::add);

            //每个月的还款本金
            lendReturn.setPrincipal(sumPrincipal);
            // 每个月还款利息
            lendReturn.setInterest(sumInterest);
            //每个月还款 本息
            lendReturn.setTotal(sumTotal);


        }

        lendReturnService.updateBatchById(lendReturnList);



    }

    /**
     * 回款计划的方法
     * @param lendItemId 标的项的id
     * @param lendReturnMap  还款期数 和还款计划的id 对应的map
     * @param lend  标的
     * @return
     */
    private List<LendItemReturn> returnInvest(Long lendItemId,
                                              Map<Integer, Long> lendReturnMap,
                                              Lend lend) {

        //投标信息对象
        LendItem lendItem = lendItemService.getById(lendItemId);
        //获取投标金额
        BigDecimal investAmount = lendItem.getInvestAmount();
        //年化率
        BigDecimal lendYearRate = lendItem.getLendYearRate();
        //期数
        Integer period = lend.getPeriod();
        //还款    key期数  value 利息
        Map<Integer,BigDecimal> mapInvest=null;
        //还款  key期数  value本金
        Map<Integer,BigDecimal> mapPrincipal= null;

        // 利息 + 本金 = 月供
        if(lend.getReturnMethod().intValue()==ReturnMethodEnum.ONE.getMethod()){//等额本息
            //利息
            mapInvest = Amount1Helper.getPerMonthInterest(investAmount,lendYearRate,period);
            mapPrincipal= Amount1Helper.getPerMonthPrincipal(investAmount,lendYearRate,period);

        }else   if(lend.getReturnMethod().intValue()==ReturnMethodEnum.TWO.getMethod()){//等额本息
            //利息
            mapInvest = Amount2Helper.getPerMonthInterest(investAmount,lendYearRate,period);
            mapPrincipal= Amount2Helper.getPerMonthPrincipal(investAmount,lendYearRate,period);

        }else   if(lend.getReturnMethod().intValue()==ReturnMethodEnum.THREE.getMethod()){//等额本息
            //利息
            mapInvest = Amount3Helper.getPerMonthInterest(investAmount,lendYearRate,period);
            mapPrincipal= Amount3Helper.getPerMonthPrincipal(investAmount,lendYearRate,period);

        }else   if(lend.getReturnMethod().intValue()==ReturnMethodEnum.FOUR.getMethod()){//等额本息
            //利息
            mapInvest = Amount4Helper.getPerMonthInterest(investAmount,lendYearRate,period);
            mapPrincipal= Amount4Helper.getPerMonthPrincipal(investAmount,lendYearRate,period);

        }

        //创建回款计划列表
        List<LendItemReturn> lendItemReturnList = new ArrayList<>();

        for(Map.Entry<Integer,BigDecimal> entry:mapInvest.entrySet()){
            //期数
            Integer currentPeriod = entry.getKey();
            //可以根据期数获取还款计划的id
            Long lendReturnId = lendReturnMap.get(currentPeriod);
            //标的出借回款记录表
            LendItemReturn lir = new LendItemReturn();
            lir.setLendReturnId(lendReturnId);//标的的还款id
            lir.setLendItemId(lendItemId);//标的项的id
            lir.setInvestUserId(lendItem.getInvestUserId());//出借用户的id
            lir.setInvestAmount(lendItem.getInvestAmount());//借了多少钱
            lir.setCurrentPeriod(currentPeriod);//期数
            lir.setLendYearRate(lend.getLendYearRate());//年化率
            lir.setReturnMethod(lend.getReturnMethod());//还款方式
            //判断 是否是最后一次还款
            if(currentPeriod.intValue()==lend.getPeriod().intValue()){
                  //成立
                //最后一期的 本金 = 本金 - 前几次之和
                BigDecimal sumPrincipal = lendItemReturnList.stream().
                        map(LendItemReturn::getPrincipal).reduce(BigDecimal.ZERO,BigDecimal::add);

                BigDecimal lastPrincipal = lendItem.getInvestAmount().subtract(sumPrincipal);
                lir.setPrincipal(lastPrincipal);


                //最后一期的 本金 = 本金 - 前几次之和
                BigDecimal sumInterest = lendItemReturnList.stream().
                        map(LendItemReturn::getInterest).reduce(BigDecimal.ZERO,BigDecimal::add);

                BigDecimal lastInterest = lendItem.getExpectAmount().subtract(sumInterest);
                lir.setInterest(lastInterest);


            }else{
                //不是最后一期 就是 本金  利息 该是多少就多少
                lir.setPrincipal(mapPrincipal.get(currentPeriod));
                lir.setInterest(mapInvest.get(currentPeriod));

            }

            //本息
            lir.setTotal(lir.getPrincipal().add(lir.getInterest()));
            //手续费
            lir.setFee(new BigDecimal(0));
            //还款时指定的还款日期
            lir.setReturnDate(lend.getLendStartDate());
            //是否逾期
            lir.setOverdue(false);
            lir.setStatus(0);//状态


            lendItemReturnList.add(lir);

        }

        //批量保存
        lendItemReturnService.saveBatch(lendItemReturnList);

        return  lendItemReturnList;
    }


}
